package info.heidao.ww2.framework.handler;

import cn.hutool.core.util.StrUtil;
import info.heidao.ww2.common.constants.ResultCodeEnum;
import info.heidao.ww2.common.constants.WebErrorInfoEnum;
import info.heidao.ww2.common.response.JsonResponse;
import info.heidao.ww2.framework.exception.AuthException;
import info.heidao.ww2.framework.exception.BizException;
import info.heidao.ww2.framework.exception.ForbiddenException;
import info.heidao.ww2.framework.exception.ParamException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import javax.servlet.http.HttpServletResponse;
import javax.validation.ValidationException;
import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;


/**
 * 接口全局异常处理
 *
 * @author zhengh
 */
@Slf4j
@RestControllerAdvice
public class WebGlobalErrorHandler extends ResponseEntityExceptionHandler {

    @Override
    protected ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        log.warn("参数绑定异常, request:[{}],msg:[{}]", request, ex.getMessage());
        return new ResponseEntity<>(JsonResponse.build(ResultCodeEnum.PARAM_ERROR.getCode(), ResultCodeEnum.PARAM_ERROR.getMsg()), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(ValidationException.class)
    public ResponseEntity<JsonResponse<Void>> handle(ValidationException exception, HttpServletResponse response) {
        response.reset();
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        return new ResponseEntity<>(JsonResponse.build(ResultCodeEnum.PARAM_ERROR.getCode(),
                StrUtil.split(exception.getMessage(), ',').stream().map(p -> StrUtil.subAfter(p, ":", false)).collect(Collectors.joining(","))),
                headers,
                HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<JsonResponse<Void>> exceptionHandle(Exception ex, WebRequest request, HttpServletResponse response) {
        response.reset();
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        //鉴权异常
        if (ex instanceof AuthException) {
            log.warn("会话失效或未认证, request:[{}],msg:[{}]", request, ex.getMessage());
            AuthException authException = (AuthException) ex;
            return new ResponseEntity<>(
                    JsonResponse.build(ResultCodeEnum.AUTH_ERROR.getCode(), authException.getMsg()), headers, HttpStatus.UNAUTHORIZED);
        }
        if (ex instanceof ForbiddenException) {
            log.warn("无权限操作, request:[{}],msg:[{}]", request, ex.getMessage());
            return new ResponseEntity<>(
                    JsonResponse.build(ResultCodeEnum.FORBIDDEN_ERROR.getCode(), ex.getMessage()), headers, HttpStatus.FORBIDDEN);
        }
        //参数异常
        if (ex instanceof ParamException) {
            log.warn("出现参数异常, request:[{}],msg:[{}]", request, ex.getMessage());
            ParamException paramException = (ParamException) ex;

            return new ResponseEntity<>(
                    JsonResponse.build(ResultCodeEnum.PARAM_ERROR.getCode(), paramException.getMsg()), headers, HttpStatus.BAD_REQUEST);
        }

        //业务异常
        if (ex instanceof BizException) {
            log.warn("出现业务异常, request:{}", request, ex);
            BizException bizException = (BizException) ex;
            return new ResponseEntity<>(
                    JsonResponse.build(ResultCodeEnum.BIZ_ERROR.getCode(), bizException.getMsg()), headers, HttpStatus.OK);
        }
        //除了业务系统异常
        log.error("出现系统异常, request:{}", request, ex);
        return new ResponseEntity<>(
                JsonResponse.build(ResultCodeEnum.SYS_ERROR.getCode(), ResultCodeEnum.SYS_ERROR.getMsg()), headers, HttpStatus.INTERNAL_SERVER_ERROR);
    }


    @Override
    protected ResponseEntity<Object> handleHttpMessageNotReadable(
            HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        return commonResult(ResultCodeEnum.PARAM_ERROR, WebErrorInfoEnum.BAD_REQUEST);
    }

    @Override
    protected ResponseEntity<Object> handleMissingServletRequestParameter(
            MissingServletRequestParameterException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        return commonResult(ResultCodeEnum.PARAM_ERROR, WebErrorInfoEnum.BAD_REQUEST);
    }

    @Override
    public ResponseEntity<Object> handleMethodArgumentNotValid(
            MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        String errorMsg = ex.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(","));
        return new ResponseEntity<>(JsonResponse.build(ResultCodeEnum.PARAM_ERROR.getCode(), errorMsg), HttpStatus.BAD_REQUEST);
    }

    @Override
    public ResponseEntity<Object> handleHttpRequestMethodNotSupported(
            HttpRequestMethodNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        return commonResult(ResultCodeEnum.PARAM_ERROR, WebErrorInfoEnum.METHOD_NOT_ALLOWED);
    }

    @Override
    protected ResponseEntity<Object> handleHttpMediaTypeNotSupported(
            HttpMediaTypeNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {

        return commonResult(ResultCodeEnum.PARAM_ERROR, WebErrorInfoEnum.METHOD_NOT_ALLOWED);
    }

    @Override
    public ResponseEntity<Object> handleNoHandlerFoundException(
            NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        return commonResult(ResultCodeEnum.PARAM_ERROR, WebErrorInfoEnum.NOT_FOUND_RESOURCES);
    }


    @Override
    public ResponseEntity<Object> handleExceptionInternal(
            Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
        log.error("出现未知异常,request:{}", request, ex);
        return commonResult(ResultCodeEnum.SYS_ERROR, WebErrorInfoEnum.UNKNOWN_ERROR);
    }


    private static ResponseEntity<Object> commonResult(ResultCodeEnum resultCodeEnum, WebErrorInfoEnum error) {
        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        return new ResponseEntity<>(JsonResponse.build(resultCodeEnum.getCode(), error.getMsg()), headers, HttpStatus.valueOf(error.getHttpStatus()));
    }
}
